﻿using Bit.Art.Api.Models;
using Bit.Art.Core;
using Bit.Art.Core.Extensions;
using Bit.Art.Core.Models;
using Bit.Art.Distributed;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NetMQ;
using NetMQ.Sockets;
using Serilog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Bit.Art.Api.Controllers
{
    /// <summary>
    /// 交易方面的API，需要身份认证
    /// </summary>
    [Authorize(Policy = "market")]
    public class MarketController : BaseController
    {
        private static Memcache s_Memcache;
        internal static Memcache Memcache
        {
            get
            {
                if (s_Memcache == null)
                {
                    s_Memcache = Memcache.Create(Utility.Configuration["redis"]);
                }

                return s_Memcache;
            }
        }

        private static volatile int OpenedSocketNo = 0;

        static MarketController()
        {
            try
            {
                using (var sink = new PushSocket(string.Format(">{0}", SiteConfiguration.MatchResultSinkEndpoint)))
                {
                    sink.SendFrame("0");
                }
                Log.Logger.Information("async socket start");
                AsyncSocket.Instance.Start();
            }
            catch (Exception e)
            {
                Console.WriteLine($"Static ctor error: {e.Message}, stack: {e.StackTrace}");
                Log.Logger.Error($"Static ctor error: {e.Message}, stack: {e.StackTrace}");
            }
        }

#if UNITTEST
        /// <summary>
        /// 只用在测试用例方面
        /// </summary>
        /// <param name="memcache"></param>
        [HttpGet]
        [Route("TestCaseOnlySetMemcache")]
        public void SetMemcache(Memcache memcache)
        {
            s_Memcache = memcache;
        }
#endif
        
        /// <summary>
        /// 在指定的交易对里下达限价买单
        /// </summary>
        /// <param name="market">交易对，格式为：coin/quote，如以USDT购买BTC的交易对格式为：BTC/USDT。</param>
        /// <param name="price">价格</param>
        /// <param name="quantity">数量</param>
        /// <returns>返回限价订单的id</returns>
        [HttpGet]
        public async Task<ApiCallResult<string>> BuyLimit(string market, decimal price, decimal quantity)
        {
            return await SubmitOrder(market, price, quantity, TradeSide.Buy);
        }

        /// <summary>
        /// 在指定的交易对里下达限价卖单
        /// </summary>
        /// <param name="market">交易对，格式为：coin/quote，如以USDT购买BTC的交易对格式为：BTC/USDT。</param>
        /// <param name="price">价格</param>
        /// <param name="quantity">数量</param>
        /// <returns>返回限价订单的id</returns>
        [HttpGet]
        public async Task<ApiCallResult<string>> SellLimit(string market, decimal price, decimal quantity)
        {
            return await SubmitOrder(market, price, quantity, TradeSide.Sell);
        }

        private async Task<ApiCallResult<string>> SubmitOrder(string market, decimal price, decimal quantity, TradeSide side)
        {
            var result = new ApiCallResult<string>();
            if (string.IsNullOrWhiteSpace(market)) throw new ArgumentNullException(nameof(market));
            if (price <= 0) throw new ArgumentOutOfRangeException(nameof(price));
            if (quantity <= 0) throw new ArgumentOutOfRangeException(nameof(quantity));
            
            try
            {
                var order = new Order()
                {
                    Market = market,
                    Side = (uint)side,
                    OrderType = (uint)OrderType.Limit,
                    Price = price,
                    Volume = quantity,
                    VolumeRemaining = quantity,
                    PlacedDate = DateTime.Now,
                    UserId = LoginUid,
                    QuoteVolume = price * quantity
                };
                
                if (side == TradeSide.Buy)
                    order.LockedBalance = checked(price * quantity * (1 + order.GetFeeRate()));
                else
                    order.LockedBalance = quantity;

                System.Threading.Interlocked.Increment(ref OpenedSocketNo);
                
                var msg = new NetMQMessage();
                msg.Append(order.ToFixedBytes());
                Log.Logger.Debug($"[API] SubmitOrder No: {OpenedSocketNo}, price: {price}, quantity: {quantity}, side: {side}");
                var replyMessage = await AsyncSocket.Instance.SendAndReceiveAsync(msg);
                var data = replyMessage.Pop().Buffer;
                System.Threading.Interlocked.Decrement(ref OpenedSocketNo);
                result.Success = true;
                result.Result = new Guid(data).ToString();
                Log.Logger.Debug($"[API] SubmitOrder No: {OpenedSocketNo}, price: {price}, quantity: {quantity}, side: {side}, ret: {result.Result}");
            }
            catch (Exception e)
            {
                result.Success = false;
                result.Message = e.Message;
                Console.WriteLine($"Static ctor error: {e.Message}, stack: {e.StackTrace}");
                Log.Logger.Error($"[API] SubmitOrder exception: {e.Message}, stack: {e.StackTrace}");
            }

            return result;
        }
        
        /// <summary>
        /// 取消一个订单
        /// </summary>
        /// <param name="market">交易对</param>
        /// <param name="id">订单id</param>
        /// <returns>返回订单是否成功取消</returns>
        [HttpGet]
        public async Task<ApiCallResult<bool>> Cancel(string market, string id)
        {
            var result = new ApiCallResult<bool>();
            if (Guid.TryParse(id, out Guid oid))
            {
                Log.Logger.Debug($"[API] Cancel id: {id}");
                var ret = await Memcache.RemoveOpenOrder(LoginUid, oid);

                var client = new RequestSocket(string.Format($">{SiteConfiguration.CancelOrderEndpoint}"));
                try
                {
                    client.Options.Identity = oid.ToByteArray();
                    var order = new Order() { Market = market, Id = oid, IsCancelled = true };
                    var msg = new NetMQMessage();
                    msg.Append(order.ToFixedBytes());
                    client.SendMultipartMessage(msg);

                    var step1 = ret && await Task.Run(() =>
                    {
                        var res = client.ReceiveMultipartMessage();
                        var data = res.Last.Buffer;
                        var success = data[0] == 1;
                        return success;
                    });

                    result.Success = step1;
                }
                catch (Exception e)
                {
                    Log.Logger.Error($"[API] Cancel exception: {e.Message}, stack: {e.StackTrace}");
                    result.Message = e.Message;
                }
                finally
                {
                    if (client != null) client.Dispose();
                }
            }
            else
            {
                result.Message = "无法解析订单号";
            }

            return result;
        }

        /// <summary>
        /// 获取一个订单详情
        /// </summary>
        /// <param name="market">交易对</param>
        /// <param name="id">订单id</param>
        /// <returns>订单的详细信息</returns>
        [HttpGet]
        public async Task<ApiCallResult<ExchangeOrderInfo>> GetOrder(string market, string id)
        {
            var acr = new ApiCallResult<ExchangeOrderInfo>();
            try
            {
                do
                {
                    if (string.IsNullOrWhiteSpace(market))
                    {
                        acr.ErrorCode = Constants.ErrorCodes.ARGUMENT_ERROR;
                        acr.Message = nameof(market);
                    }
                    if (string.IsNullOrWhiteSpace(id))
                    {
                        acr.ErrorCode = Constants.ErrorCodes.ARGUMENT_ERROR;
                        acr.Message = nameof(id);
                    }
                    if (acr.ErrorCode != 0) break;

                    using (var client = new RequestSocket(string.Format($">{SiteConfiguration.DatabaseEndpoint}")))
                    {
                        var msg = new GetOrderDetailsMessage()
                        {
                            Market = market,
                            UserId = LoginUid,
                            OrderId = id
                        };

                        client.SendFrame(msg.ToJson());
                        acr = await Task.Run(() =>
                        {
                            var res = client.ReceiveMultipartMessage();
                            var json = res.Last.ConvertToString(System.Text.Encoding.UTF8);

                            return Utility.From<ApiCallResult<ExchangeOrderInfo>>(json);
                        });
                    };
                }
                while (false);
            }
            catch (Exception e)
            {
                acr.Success = false;
                acr.Message = e.Message;
                acr.ErrorCode = Constants.ErrorCodes.SERVER_ERROR;
                Log.Logger.Error(e, "[GetOrder] 获取用户订单详情发生错误");
            }

            return acr;
        }

        /// <summary>
        /// 获取未撮合成功的订单列表
        /// </summary>
        /// <param name="market">交易对</param>
        /// <returns>未撮合成功的订单列表</returns>
        [HttpGet]
        public async Task<ApiCallResult<ExchangeOrderInfo[]>> GetOpenOrders(string market)
        {
            var response = new ApiCallResult<ExchangeOrderInfo[]>();
            
            try
            {
                var orders = await Memcache.GetOpenOrders(LoginUid);
                var result = new List<ExchangeOrderInfo>();
                if (orders != null)
                {
                    foreach (var order in orders)
                    {
                        result.Add(new ExchangeOrderInfo()
                        {
                            Market = market,
                            Id = order.Id.ToString(),
                            Price = order.Price,
                            PlacedTimestamp = order.PlacedDate,
                            Quantity = order.Volume,
                            QuantityRemaining = order.VolumeRemaining,
                            Side = (TradeSide)order.Side
                        });
                    }
                }
                response.Success = true;
                response.Result = result.ToArray();
            }
            catch (Exception e)
            {
                Log.Logger.Error($"[API] GetOpenOrders exception: {e.Message}, stack: {e.StackTrace}");
                response.Message = e.Message;
            }

            return response;
        }

        /// <summary>
        /// 获取用户的余额
        /// </summary>
        /// <returns>返回用户所有的余额</returns>
        [HttpGet]
        public async Task<ApiCallResult<BalanceInfo[]>> GetBalance()
        {
            var response = new ApiCallResult<BalanceInfo[]>();

            try
            {
                var cached = await Memcache.GetAllBalances(LoginUid);
                var result = cached.Select(c => new BalanceInfo()
                {
                    Coin = c.Currency,
                    Balance = c.Balance,
                    Pending = c.Pending,
                    Available = c.Available
                }).ToArray();
                response.Success = true;
                response.Result = result;
            }
            catch (Exception e)
            {
                response.Success = false;
                response.Result = null;
                response.Message = e.Message;
                Log.Logger.Error($"[API] GetBalance exception: {e.Message}, stack: {e.StackTrace}");
            }

            return response;
        }
    }
}